package com.starview.star.compiler.type;

import com.starview.platform.data.type.IType;
import com.starview.platform.data.type.TypeExp;
import com.starview.platform.data.type.TypeUtils;
import com.starview.platform.data.type.TypeVar;
import com.starview.star.compiler.ast.Abstract;
import com.starview.star.compiler.ast.IAbstract;
import com.starview.star.compiler.standard.StandardNames;

public class TypeContracts
{
  public static String contractImplTypeName(String name)
  {
    assert !name.startsWith("@");
    return /* "@" + */name;
  }

  
  // parse <con> over <tp>
  // into con#tp

  public static String contractImplName(IAbstract tp)
  {
    StringBuilder blder = new StringBuilder();

    if (Abstract.isBinary(tp, StandardNames.OVER) || Abstract.isBinary(tp, StandardNames.OF)) {
      IAbstract con = Abstract.binaryLhs(tp);
      IAbstract tpTerm = Abstract.binaryRhs(tp);

      blder.append(tpName(con));
      blder.append("#");
      blder.append(tpName(tpTerm));
    }

    return blder.toString();
  }

  private static String tpName(IAbstract tp)
  {
    if (Abstract.isIdentifier(tp))
      return Abstract.getId(tp);
    else if (Abstract.isBinary(tp, StandardNames.OF) && Abstract.isIdentifier(Abstract.binaryLhs(tp)))
      return Abstract.getId(Abstract.binaryLhs(tp));
    else
      return tp.toString();
  }

  public static TypeExp contractImplType(TypeExp contract)
  {
    assert !contract.typeLabel().startsWith("@");
    return (TypeExp) TypeUtils.typeExp(contractImplTypeName(contract.typeLabel()), contract.getTypeArgs());
  }

  public static TypeExp contractFallbackImplType(TypeExp contract)
  {
    assert !contract.typeLabel().startsWith("@");
    IType[] args = new IType[contract.typeArity()];
    for (int ix = 0; ix < args.length; ix++)
      args[ix] = new TypeVar();
    return (TypeExp) TypeUtils.typeExp(contractImplTypeName(contract.typeLabel()), args);
  }

  public static String contractFallbackName(IType contract)
  {
    assert !contract.typeLabel().startsWith("@");
    return "@" + contractImplTypeName(contract.typeLabel()) + "#@@";
  }

  public static boolean isContractFallbackName(String name)
  {
    return name.startsWith("@") && name.endsWith("#@@");
  }

  public static IType actualContractType(IType type)
  {
    type = TypeUtils.deRef(type);
    if (type instanceof TypeExp) {
      TypeExp conType = (TypeExp) type;
      IType[] typeArgs = conType.getTypeArgs();
      for (int ix = 0; ix < typeArgs.length; ix++) {
        IType tp = TypeUtils.deRef(typeArgs[ix]);
        if (TypeUtils.isType(tp, StandardNames.DETERMINES)) {
          IType aTypeArgs[] = new IType[ix];
          System.arraycopy(typeArgs, 0, aTypeArgs, 0, ix);
          return TypeUtils.typeExp(conType.typeLabel(), aTypeArgs);
        }
      }
      return conType;
    } else
      return type;
  }
}
